/*
 * SPDX-FileCopyrightText: 2010-2022 Espressif Systems (Shanghai) CO LTD
 * SPDX-License-Identifier: CC0-1.0
 */
/*  funb.fun同步更新
C 语言代码的头文件包含部分。其中包括了
标准输入输出头文件 <stdio.h>、
整数类型头文件 < inttypes.h>、SDK
配置头文件 "sdkconfig.h"、
FreeRTOS 的任务相关头文件 "freertos/FreeRTOS.h" 和 "freertos/task.h"、
ESP32 芯片信息头文件 "esp_chip_info.h"
以及 ESP32 闪存相关头文件 "esp_flash.h"。
gpio.h 是 ESP32 芯片的 GPIO 控制头文件，
my_i2c.h 是自定义的 I2C 通信头文件，
gxhtc3.h 是自定义的 GXHT3 温湿度传感器驱动头文件。
esp_log.h 是 ESP-IDF 中用于日志输出的头文件。
这些头文件提供了不同的功能和接口，用于在程序中进行输入输出、处理特定整数类型、
获取芯片信息、进行闪存操作以及使用 FreeRTOS 进行多任务处理等。 */

#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
// 第二课 通用输入输出
#include "driver/gpio.h"

// 第三课 温湿度传感器
#include "myi2c.h" // 自定义的 I2C 通信头文件
// #include "gxhtc3.h"  // 自定义的 GXHT3 温湿度传感器驱动头文件
#include "esp_log.h" // 用于 ESP-IDF 中的日志输出

// 第四课 姿态控制器
#include "qmi8658c.h"                       // 自定义的 QMI8658C 传感器驱动头文件
t_sQMI8658C QMI8658C;                       // QMI8658C 传感器结构体变量
static const char *标签 = "姿态控制程序";    // 用于 ESP-IDF 中的日志输出

/* //第三课内容 温湿度传感器
extern float 温度, 湿度;
extern uint8_t 整数温度, 整数湿度;
static void gxhtc3_显示(void *args) // 函数声明，用于显示温湿度数据
{
    esp_err_t 返回值;

    while (1)
    {
        返回值 = gxhtc3_取温湿度();
        if (返回值 != ESP_OK)
        {
            // ESP_LOGI 是 ESP-IDF 中用于打印日志信息的宏
            ESP_LOGE(标签, "GXHTC3 读取 TAH 错误。%s", esp_err_to_name(返回值));
        }
        else
        {
            ESP_LOGI(标签, "温度:%.1f 湿度:%.1f", 温度, 湿度);
            ESP_LOGI(标签, "温度:%d 湿度:%d", 整数温度, 整数湿度);
        }
        vTaskDelay(1000 / portTICK_PERIOD_MS);
        // vTaskDelay 是 FreeRTOS 函数，用于在任务中进行延迟，portTICK_PERIOD_MS单位是毫秒
    }
} */

// 第二课 gpio_evt_queue 通用输入输出中断事件队列
// IRAM_ATTR内部存储器_属性
// gpio_isr_handler 通用输入输出中断服务
// gpio_num 通用输入输出引脚号的指针
// QueueHandle_t 是 FreeRTOS 中用于表示队列句柄的类型
static QueueHandle_t GPIO事件队列 = NULL;

// IRAM_ATTR 是 ESP-IDF 中定义的一个宏，用于指定函数在内部存储器中运行
static void IRAM_ATTR GPIO中断服务(void *参数)
{
    uint32_t GPIO引脚 = (uint32_t)参数;
    // xQueueSendFromISR 是 FreeRTOS 函数，用于在中断服务程序中向队列发送消息
    xQueueSendFromISR(GPIO事件队列, &GPIO引脚, NULL);
}

/**
 * @brief GPIO任务示例函数
 *
 * 该函数用于处理GPIO中断事件队列中的消息，并打印GPIO引脚的中断状态和电平值。
 *
 * @param arg 传递给任务的参数，这里未使用
 */
static void GPIO任务示例(void *参数)
{
    uint32_t 引脚号; // 用于存储接收到的GPIO引脚号
    for (;;)
    { // 无限循环，不断处理队列中的消息
        // xQueueReceive 函数用于从一个队列中接收消息，从gpio_evt_queue队列中接收消息，阻塞直到有消息可用
        if (xQueueReceive(GPIO事件队列, &引脚号, portMAX_DELAY))
        {
            // 打印GPIO引脚的中断信息和当前电平值，
            // % PRIu32 是一个格式说明符，用于打印无符号长整型变量，可以确保在不同平台上都能正确地输出无符号 32 位整数，提高了程序的可移植性
            // gpio_get_level 函数是 ESP-IDF 中提供的函数，用于获取 GPIO 引脚的电平值
            printf("GPIO引脚号[%" PRIu32 "] intr中断, val电平值: %d\n", 引脚号, gpio_get_level(引脚号));

            /*             // 第一课 重启
                        for (int i = 10; i >= 0; i--)
                        {
                            printf("在 % d 秒内重新启动…\n", i);
                            // vTaskDelay 是 FreeRTOS 函数，用于在任务中进行延迟，portTICK_PERIOD_MS单位是毫秒
                            vTaskDelay(1000 / portTICK_PERIOD_MS);
                        }
                        printf("现在重启.\n");
                        // fflush 函数用于刷新输出缓冲区，确保所有输出都被写入到输出设备中
                        fflush(stdout);
                        // esp_restart 函数用于重启 ESP32 芯片，在 esp_restart 函数之后的代码将不会被执行
                        esp_restart();
                        // 第一课 重启 */

            /*             // 第三课 温湿度传感器
                        ESP_ERROR_CHECK(i2c_master_init()); // 调用初始化函数，并且用ESP_ERROR_CHECK 检测是否初始化成功。
                        ESP_LOGI(标签, "I2C初始化成功");    // 相当于是printf函数k, ESP_LOGI 是 ESP-IDF 中用于打印日志信息的宏

                        esp_err_t 返回值 = gxhtc3_读取id(); // 调用初始化函数,并且用ESP_ERROR_CHECK 检测是否初始化成功。
                        while (返回值 != ESP_OK)              // 检测是否初始化成功。
                        {
                            返回值 = gxhtc3_读取id();
                            ESP_LOGI(标签, "GXHTC3读取ID%s", esp_err_to_name(返回值));
                            vTaskDelay(1000 / portTICK_PERIOD_MS);
                        }
                        ESP_LOGI(标签, "GXHTC3 OK");

                        xTaskCreate(gxhtc3_显示, "gxhtc3任务", 4096, NULL, 6, NULL);
                        // xTaskCreate 是 FreeRTOS 函数，用于创建一个任务
                        // 第三课 温湿度传感器 */

            // 第四课 姿态控制器
            ESP_ERROR_CHECK(i2c_master_init()); // 调用初始化函数，并且用ESP_ERROR_CHECK 检测是否初始化成功。
            ESP_LOGI(标签, "I2C初始化成功");    // 相当于是printf函数k, ESP_LOGI 是 ESP-IDF 中用于打印日志信息的宏

            qmi8658c_init();                    //  调用初始化函数，并且用ESP_ERROR_CHECK 检测是否初始化成功。

            while (1)
            {
                vTaskDelay(1000 / portTICK_PERIOD_MS);  // 延时1s   
                qmi8658c_fetch_angleFromAcc(&QMI8658C); // 调用初始化函数，并且用ESP_ERROR_CHECK 检测是否初始化成功。  
                ESP_LOGI(标签, "角度x = %.1f  角度y = %.1f 角度y = %.1f", QMI8658C.角度X, QMI8658C.角度Y, QMI8658C.角度Z); 
                // 调用初始化函数，并且用ESP_ERROR_CHECK 检测是否初始化成功。
                // 第四课 姿态控制器
            }
        }
    }
}

void app_main(void)
{
    printf("Hello SoPlus! 你好索加! \n");
    /* 打印芯片信息
    esp_chip_info_t 类型名，后接的chip_info可以改名字。
    cores 核心数，

    */
    esp_chip_info_t 芯片信息;
    uint32_t flash_size;
    esp_chip_info(&芯片信息);
    printf("这是 %s 芯片内含 %d CPU 核心, %s%s%s%s, ",
           CONFIG_IDF_TARGET,
           芯片信息.cores,
           (芯片信息.features & CHIP_FEATURE_WIFI_BGN) ? "WiFi/" : "",
           (芯片信息.features & CHIP_FEATURE_BT) ? "BT" : "",
           (芯片信息.features & CHIP_FEATURE_BLE) ? "BLE" : "",
           (芯片信息.features & CHIP_FEATURE_IEEE802154) ? ", 802.15.4 (Zigbee/Thread)" : "");

    unsigned major_rev = 芯片信息.revision / 100;
    unsigned minor_rev = 芯片信息.revision % 100;
    printf("“硅版本修订 v%d.%d, ", major_rev, minor_rev);
    if (esp_flash_get_size(NULL, &flash_size) != ESP_OK)
    {
        printf("获取闪存大小失败");
        return;
    }

    printf("%" PRIu32 "MB %s flash\n", flash_size / (uint32_t)(1024 * 1024),
           (芯片信息.features & CHIP_FEATURE_EMB_FLASH) ? "内置" : "外接");

    printf("堆内存的最小可用大小: %" PRIu32 " 字节\n", esp_get_minimum_free_heap_size());

    /*     for (int i = 10; i >= 0; i--) {
            printf("在 % d 秒内重新启动…\n", i);
            vTaskDelay(1000 / portTICK_PERIOD_MS);
        }
        printf("现在重启.\n");
        fflush(stdout);
        esp_restart(); */

    // 第二课 零初始化配置结构
    gpio_config_t IO_配置 = {
        // GPIO_INTR_NEGEDGE 是 ESP-IDF 中定义的一个宏，用于设置 GPIO 引脚的中断类型为下降沿触发
        .intr_type = GPIO_INTR_NEGEDGE,
        // GPIO_MODE_INPUT 是 ESP-IDF 中定义的一个宏，用于设置 GPIO 引脚的模式为输入模式
        .mode = GPIO_MODE_INPUT,
        // 1ULL 是一个无符号长整型常量，表示 1 左移 9 位，即 2 的 9 次方。
        // 1ULL << GPIO_NUM_9 表示将 1 左移 GPIO_NUM_9 位，即 GPIO_NUM_9 对应的 GPIO 引脚的位掩码
        .pin_bit_mask = 1ULL << GPIO_NUM_9,
        // 0 表示禁用下拉电阻，1 表示启用下拉电阻
        .pull_down_en = 0,
        .pull_up_en = 1};
    //  ESP-IDF 使用 gpio_config 给定的设置配置 GPIO
    gpio_config(&IO_配置);

    // xQueueCreate 是 FreeRTOS 函数，用于创建一个队列
    GPIO事件队列 = xQueueCreate(10, sizeof(uint32_t));

    // xTaskCreate 是 FreeRTOS 函数，用于创建一个任务
    xTaskCreate(GPIO任务示例, "GPIO任务示例", 2048, NULL, 10, NULL);

    // gpio_install_isr_service 是 ESP-IDF 中用于安装 GPIO 中断服务程序的函数
    gpio_install_isr_service(0);

    // gpio_isr_handler_add 是 ESP-IDF 中用于将中断服务程序与特定的 GPIO 引脚关联的函数
    gpio_isr_handler_add(GPIO_NUM_9, GPIO中断服务, (void *)GPIO_NUM_9);
}
